for mut child in repo.submodules()?.into_iter() {
update_submodule(repo, &mut child, cargo_config)
- .map_err(CargoError::to_internal)
+ .map_err(CargoError::into_internal)
.chain_err(|| {
format!("failed to update submodule `{}`",
child.name().unwrap_or(""))
// In the case of an authentication failure (where we tried something) then
// we try to give a more helpful error message about precisely what we
// tried.
- res.map_err(CargoError::from).map_err(|e| e.to_internal()).chain_err(|| {
+ res.map_err(CargoError::from).map_err(|e| e.into_internal()).chain_err(|| {
let mut msg = "failed to authenticate when downloading \
repository".to_string();
if !ssh_agent_attempts.is_empty() {
}
errors {
- Internal(description: String){
- description(&description)
- display("{}", &description)
+ Internal(err: Box<CargoErrorKind>) {
+ description(err.description())
+ display("{}", *err)
}
ProcessErrorKind(proc_err: ProcessError) {
description(&proc_err.desc)
}
impl CargoError {
- pub fn to_internal(self) -> Self {
- //This is actually bad, because it loses the cause information for foreign_link
- CargoError(CargoErrorKind::Internal(self.description().to_string()), self.1)
+ pub fn into_internal(self) -> Self {
+ CargoError(CargoErrorKind::Internal(Box::new(self.0)), self.1)
}
fn is_human(&self) -> bool {
}
fn _internal(error: &fmt::Display) -> CargoError {
- CargoErrorKind::Internal(error.to_string()).into()
+ CargoError::from_kind(error.to_string().into()).into_internal()
}
+use std;
+use std::error::Error;
+
+use error_chain::ChainedError;
+
use util::Config;
use util::errors::{CargoError, CargoErrorKind, CargoResult};
use git2;
+fn maybe_spurious<E, EKind>(err: &E) -> bool
+ where E: ChainedError<ErrorKind=EKind> + 'static {
+ //Error inspection in non-verbose mode requires inspecting the
+ //error kind to avoid printing Internal errors. The downcasting
+ //machinery requires &(Error + 'static), but the iterator (and
+ //underlying `cause`) return &Error. Because the borrows are
+ //constrained to this handling method, and because the original
+ //error object is constrained to be 'static, we're casting away
+ //the borrow's actual lifetime for purposes of downcasting and
+ //inspecting the error chain
+ unsafe fn extend_lifetime(r: &Error) -> &(Error + 'static) {
+ std::mem::transmute::<&Error, &Error>(r)
+ }
-fn maybe_spurious(err: &CargoError) -> bool {
- match err.kind() {
- &CargoErrorKind::Git(ref git_err) => {
- match git_err.class() {
- git2::ErrorClass::Net |
- git2::ErrorClass::Os => true,
- _ => false
+ for e in err.iter() {
+ let e = unsafe { extend_lifetime(e) };
+ if let Some(cargo_err) = e.downcast_ref::<CargoError>() {
+ match cargo_err.kind() {
+ &CargoErrorKind::Git(ref git_err) => {
+ match git_err.class() {
+ git2::ErrorClass::Net |
+ git2::ErrorClass::Os => return true,
+ _ => ()
+ }
}
+ &CargoErrorKind::Curl(ref curl_err)
+ if curl_err.is_couldnt_connect() ||
+ curl_err.is_couldnt_resolve_proxy() ||
+ curl_err.is_couldnt_resolve_host() ||
+ curl_err.is_operation_timedout() ||
+ curl_err.is_recv_error() => {
+ return true
+ }
+ &CargoErrorKind::HttpNot200(code, ref _url) if 500 <= code && code < 600 => {
+ return true
+ }
+ _ => ()
}
- &CargoErrorKind::Curl(ref curl_err) => {
- curl_err.is_couldnt_connect() ||
- curl_err.is_couldnt_resolve_proxy() ||
- curl_err.is_couldnt_resolve_host() ||
- curl_err.is_operation_timedout() ||
- curl_err.is_recv_error()
- }
- &CargoErrorKind::HttpNot200(code, ref _url) => {
- 500 <= code && code < 600
- }
- _ => false
+ }
}
+ false
}
/// Wrapper method for network call retry logic.
let result = with_retry(&config, || results.pop().unwrap());
assert_eq!(result.unwrap(), ())
}
+
+#[test]
+fn with_retry_finds_nested_spurious_errors() {
+ //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
+ //String error messages are not considered spurious
+ let error1 : CargoError = CargoErrorKind::HttpNot200(501, "Uri".to_string()).into();
+ let error1 = CargoError::with_chain(error1, "A non-spurious wrapping err");
+ let error2 = CargoError::from_kind(CargoErrorKind::HttpNot200(502, "Uri".to_string()));
+ let error2 = CargoError::with_chain(error2, "A second chained error");
+ let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
+ let config = Config::default().unwrap();
+ let result = with_retry(&config, || results.pop().unwrap());
+ assert_eq!(result.unwrap(), ())
+}